/*
 *  linux/lib/vsprintf.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/*
 * Wirzenius wrote this portably, Torvalds fucked it up :-)
 */

#include <string.h>
#include <sys/ctype.h>
#include <sys/errno.h>
#include <cli.h>

unsigned long simple_strtoul(const char *cp, char **endp,
                unsigned int base)
{
    unsigned long result = 0;
    unsigned long value;

    if (*cp == '0') {
        cp++;
        if ((*cp == 'x') && isxdigit(cp[1])) {
            base = 16;
            cp++;
        }

        if (!base)
            base = 8;
    }

    if (!base)
        base = 10;

    while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
        ? toupper(*cp) : *cp)-'A'+10) < base) {
        result = result*base + value;
        cp++;
    }

    if (endp)
        *endp = (char *)cp;

    return result;
}

int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
{
    char *tail;
    unsigned long val;
    size_t len;

    *res = 0;
    len = strlen(cp);
    if (len == 0)
        return -EINVAL;

    val = simple_strtoul(cp, &tail, base);
    if (tail == cp)
        return -EINVAL;

    if ((*tail == '\0') ||
        ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
        *res = val;
        return 0;
    }

    return -EINVAL;
}

long simple_strtol(const char *cp, char **endp, unsigned int base)
{
    if (*cp == '-')
        return -simple_strtoul(cp + 1, endp, base);

    return simple_strtoul(cp, endp, base);
}

unsigned long ustrtoul(const char *cp, char **endp, unsigned int base)
{
    unsigned long result = simple_strtoul(cp, endp, base);
    switch (**endp) {
    case 'G':
        result *= 1024;
        /* fall through */
    case 'M':
        result *= 1024;
        /* fall through */
    case 'K':
    case 'k':
        result *= 1024;
        if ((*endp)[1] == 'i') {
            if ((*endp)[2] == 'B')
                (*endp) += 3;
            else
                (*endp) += 2;
        }
    }
    return result;
}

unsigned long long ustrtoull(const char *cp, char **endp, unsigned int base)
{
    unsigned long long result = simple_strtoull(cp, endp, base);
    switch (**endp) {
    case 'G':
        result *= 1024;
        /* fall through */
    case 'M':
        result *= 1024;
        /* fall through */
    case 'K':
    case 'k':
        result *= 1024;
        if ((*endp)[1] == 'i') {
            if ((*endp)[2] == 'B')
                (*endp) += 3;
            else
                (*endp) += 2;
        }
    }
    return result;
}

unsigned long long simple_strtoull(const char *cp, char **endp,
                    unsigned int base)
{
    unsigned long long result = 0, value;

    if (*cp == '0') {
        cp++;
        if ((*cp == 'x') && isxdigit(cp[1])) {
            base = 16;
            cp++;
        }

        if (!base)
            base = 8;
    }

    if (!base)
        base = 10;

    while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp - '0'
        : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10) < base) {
        result = result * base + value;
        cp++;
    }

    if (endp)
        *endp = (char *) cp;

    return result;
}

long trailing_strtoln(const char *str, const char *end)
{
    const char *p;

    if (!end)
        end = str + strlen(str);
    if (isdigit(end[-1])) {
        for (p = end - 1; p > str; p--) {
            if (!isdigit(*p))
                return simple_strtoul(p + 1, NULL, 10);
        }
    }

    return -1;
}

long trailing_strtol(const char *str)
{
    return trailing_strtoln(str, NULL);
}
